package com.cx.base.day19_thread;

/**
 * @Author: robert.song
 * @Date: Created in 2021/11/28
 */


import lombok.extern.slf4j.Slf4j;

import java.time.DayOfWeek;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 1、ThreadPoolExecutors 用来创建线程池
 * 2、线程池的种类多种多样，可以根据业务选取合适种类
 * 3、四种线程池：
 * newSingleThreadExecutor :创建一个单线程化的线程池，它只会用唯一的工作线程来执行任务， 保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行
 * 适用场景：保证线程执行顺序
 * newCachedThreadPool :创建一个可缓存线程池，如果线程池长度超过处理需要，可灵活回收空闲线程，若无可回收，则新建线程.
 * 线程没有任务要执行时，便处于空闲状态，处于空闲状态的线程并不会被立即销毁（会被缓存住），只有当空闲时间超出一段时间(默认为60s)后，线程池才会销毁该线程（相当于清除过时的缓存）。新任务到达后，线程池首先会让被缓存住的线程（空闲状态）去执行任务，如果没有可用线程（无空闲线程），便会创建新的线程。
 * 适用场景：处理任务速度 > 提交任务速度,耗时少的任务(避免无限新增线程)
 * newFixedThreadPool :创建一个定长线程池，可控制线程最大并发数，超出的线程会在队列中等待。
 * newScheduledThreadPool:创建一个定长线程池，支持定时及周期性任务执行
 */
@Slf4j
public class Thread_16_1_ThreadPoolExecutors {
    public static void main(String[] args) {
//        newFixedThreadPool
//        useDefaultThreadFactory();
//        useAutoThreadFactory();
//        newCachedThreadPool
//        首先测试下CachedThreadPool的队列实例，该队列比较特殊，没有容量，实现了保护性暂停模式，进去一个任务，其余任务无法进入，必须等待消费掉
//        testSynchronousQueue();
//        newCachedThreadPool
//        useCachedThreadPool();
//        newSingleThread
//          useSingleThreadPool();
//        newScheduledThread
//        userScheduledThread();
//        设置间隔时间
        userScheduledThreadToFixedRate();
    }


    //    使用线程池的默认线程工厂实例，newFixedThreadPool内部封装了ThreadPoolExecutor方法，线程工厂已经提供
    public static void useDefaultThreadFactory() {
        ExecutorService pool = Executors.newFixedThreadPool(2);
        pool.execute(() -> {
            System.out.println(Thread.currentThread());
        });
        pool.execute(() -> {
            System.out.println(Thread.currentThread());
        });
    }

    //    使用自定义线程工厂实例
    public static void useAutoThreadFactory() {
        ExecutorService pool = Executors.newFixedThreadPool(2, new ThreadFactory() {
            private AtomicInteger t = new AtomicInteger(1);

            @Override
            public Thread newThread(Runnable r) {
                return new Thread(r, "auto_pool_" + t.getAndIncrement());
            }
        });
        pool.execute(() -> {
            System.out.println(Thread.currentThread());
        });
        pool.execute(() -> {
            System.out.println(Thread.currentThread());
        });
    }

    public static void testSynchronousQueue() {
        SynchronousQueue synchronousQueue = new SynchronousQueue();
        for (int i = 0; i < 3; i++) {
            new Thread(() -> {
                try {
                    log.info("waitting...");
                    synchronousQueue.put(1);
                    log.info("waitted");
                    Thread.sleep(5);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }, "provider_" + i).start();
        }
        for (int i = 0; i < 3; i++) {
            new Thread(() -> {
                try {
//                    cachedThreadPool原理就是这样：放进一个新任务，启用救急线程消费任务
                    synchronousQueue.take();
                    log.info("taked");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }, "consumer_" + i).start();
        }
    }

    public static void useCachedThreadPool() {
        ExecutorService pool = Executors.newCachedThreadPool();
//      任务被线程接走后，队列就接受新任务，并不是线程要把任务消费掉了才接收新任务
        pool.execute(() -> {
            for (int i = 0; i < 100000; i++) {
                if (i % 10000 == 0) {
                    System.out.println(Thread.currentThread() + ":" + i);
                }
            }
            System.out.println(Thread.currentThread());
        });
        pool.execute(() -> {
            for (int i = 0; i < 100000; i++) {
                if (i % 10000 == 0) {
                    System.out.println(Thread.currentThread() + ":" + i);
                }
            }
            System.out.println(Thread.currentThread());
        });
    }

    public static void useSingleThreadPool() {
        ExecutorService pool = Executors.newSingleThreadExecutor();
//      任务被线程接走后，队列就接受新任务，并不是线程要把任务消费掉了才接收新任务
        pool.execute(() -> {
            for (int i = 0; i < 100000; i++) {
                if (i % 10000 == 0) {
                    System.out.println(Thread.currentThread() + ":" + i);
                }
            }
            System.out.println(Thread.currentThread());
        });
        pool.execute(() -> {
            for (int i = 0; i < 100000; i++) {
                if (i % 10000 == 0) {
                    System.out.println(Thread.currentThread() + ":" + i);
                }
            }
            System.out.println(Thread.currentThread());
        });

    }

    private static void userScheduledThread() {
        ScheduledExecutorService pool = Executors.newScheduledThreadPool(1);
        pool.schedule(() -> {
            log.info("task1");
            try {
                Thread.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, 1, TimeUnit.SECONDS);
        pool.schedule(() -> {
            log.info("task1");
            try {
                Thread.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, 3, TimeUnit.SECONDS);
        pool.schedule(() -> {
            log.info("task1");
            try {
                Thread.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, 10, TimeUnit.SECONDS);
    }

    //  定时任务
    private static void userScheduledThreadToFixedRate() {
        //获取硬件支持线程数
        System.out.println(Runtime.getRuntime().availableProcessors());
//      获取当前时间
        LocalDateTime now = LocalDateTime.now();
        System.out.println(now);
//        获取要定时的时间
        LocalDateTime time = now.withHour(18).withMinute(0).withNano(0).with(DayOfWeek.THURSDAY);
//        如果当前时间>本周周四，必须找到下周周四
        if (now.compareTo(time) > 0) {
            time = time.plusWeeks(1);
        }
        System.out.println(time);
//        initial代表当前时间和周四时间差
        long initialDelay = Duration.between(now, time).toMillis();
//        执行频率，一周一次
        long period = 1000 * 60 * 60 * 24 * 7;
        ScheduledExecutorService pool = Executors.newScheduledThreadPool(1);
        pool.scheduleAtFixedRate(() -> {
            log.info("task1");
            try {
                Thread.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, initialDelay, period, TimeUnit.MILLISECONDS);
    }
}
